package com.hjk.shiny.androidcontrols;

import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.text.TextUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * Created by Maibenben on 2018/2/2.
 */

/**
 * 需要添加的权限
 *     在SDCard中创建与删除文件权限
 *     <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
 *     往SDCard写入数据权限
 *     <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 */

/**
 * File常用方法：
 * 建立文件
 * File.createNewFile();
 *
 * 建立文件夹
 * File.mkDir();
 *
 * 判断是否是文件
 * File.isFile();
 *
 * 判断是否是文件夹
 * File.isDirectory();
 *
 * 列出文件夹下的所有文件和文件夹名
 * File[] files = File.listFiles();
 *
 * //重命名文件或文件夹
 * File.renameTo(dest);
 *
 * //删除文件夹或文件
 * File.delete();
 *
 * //获得文件或文件夹的名称
 * String Name = File.getName();
 *
 * //获得文件或文件夹的父目录
 * String parentPath = File.getParent();
 *
 * //获得文件或文件夹的绝对路经
 * String path = File.getAbsoultePath();
 *
 * //获得文件或文件夹的路经
 * String path = File.getPath();
 *
 * //获得sd卡根目录
 * File skRoot = Environment.getExternalStorageDirectory();
 */

/**
 * StatFs用于获取存储空间
 *构造函数：StatFs(String path){}
 *
 * API>18时的方法
 *
 * 文件系统中可被应用程序使用的空闲存储区块的数量
 * getAvailableBlocksLong()
 *
 * 文件系统中可被应用程序使用的空闲字节数
 * getAvailableBytes()
 *
 * 文件系统中总的存储区块的数量
 * getBlockCountLong()
 *
 * 文件系统中每个存储区块的字节数
 * getBlockSizeLong()
 *
 * 文件系统中总的空闲存储区块的数量，包括保留的存储区块（不能被普通应用程序使用）
 * getFreeBlocksLong()
 *
 * 文件系统中总的空闲字节数，包括保留的存储区块（不能被普通应用程序使用）
 * getFreeBytes()
 *
 * 文件系统支持的总的存储字节数
 * getTotalBytes()
 */

/**
 * fileChannel
 * 它能直接连接输入输出流的文件通道，将数据直接写入到目标文件中去,而且效率更高。
 * FileChannel是一个用读写，映射和操作一个文件的通道。
 * 除读写操作之外，还有裁剪特定大小文件truncate()，
 * 强制在内存中的数据刷新到硬盘中去force()，
 * 对通道上锁lock()等功能。
 */
public class FileClass {

    /**
     * 判断sd卡是否挂载
     */
    public static boolean isSDcardMount() {
        //Environment.MEDIA_MOUNTED  =>  "mounted","unmounted"
        boolean result = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);

        return result;
    }

    /**
     * 获取sdcard根目录
     */
    public static String getSDcardBaseDir() {
        //调用前需先判断sdcard是否挂载
        String result = Environment.getExternalStorageDirectory().getAbsolutePath();

        return result;
    }

    /**
     * 获取sdcard大小
     */
    public static long getSDcardAllSizeKb() {
        long result = 0;
        //获取sdcard当前相对路径
        String filepath = Environment.getExternalStorageDirectory().getPath();
        //获取sdcard总大小/1024转换为kb单位
        StatFs sf = new StatFs(filepath);
        result = sf.getTotalBytes() / 1024;

        return result;
    }

    /**
     * 获取sdcard当前可用空间
     */
    public static long getSDcardAvaliableSizeKb() {
        long result = 0;
        String filePath = Environment.getExternalStorageDirectory().getPath();
        StatFs sf = new StatFs(filePath);
        //获取系统中可被应用程序使用的字节转为kb
        result = sf.getAvailableBytes() / 1024;

        return result;
    }

    /**
     * 获取文件或目录的大小
     */
    public static long getFileSize(File file) {
        long result = 0;
        //判断file是否存在
        if (file.exists()) {
            //判断目录还是文件
            if (file.isDirectory()) {
                //遍历目录所有内容
                for (File inFile : file.listFiles()) {
                    //递归读大小
                    result += getFileSize(inFile);
                }
            } else {
                result += file.length();
            }

        } else {
            throw new IllegalArgumentException("file does not exists!");
        }

        return result;
    }

    /**
     * 清空文件或目录
     * true:clear
     * false:delete
     */
    public static void clearOrDeleteFile(File file, boolean clearOrDelete) {
        //先判断存在
        if(file.exists()) {
            //再判断类型
            if (file.isDirectory()) {
                //清空删除前面的步骤相同，不同于是否留下最外层目录
                for (File inFile : file.listFiles()) {
                    clearOrDeleteFile(inFile, false);
                }
                //判断清空还是删除
                if (!clearOrDelete) {
                    //删除则去除最外层目录
                    file.delete();
                }
            } else {
                //判断清空还是删除
                if (clearOrDelete) {
                    String path = file.getPath();
                    file.delete();
                    File newFile = new File(path);
                    //异常捕获---file删除失败情况下
                    try {
                        newFile.createNewFile();
                    } catch (IOException e) {
                        System.out.println("file delete fail in clear");
                        e.printStackTrace();
                    }
                } else {
                    //删除
                    file.deleteOnExit();
                }
            }
        }else{
            throw  new IllegalArgumentException("file does not exists");
        }
    }

    /**
     *File 复制
     */
    public static boolean copyFile(String srcPath,String dstPath){
        boolean result=false;

        //判断参数
        if(srcPath==null||dstPath==null){
            return result;
        }

        File srcFile=new File(srcPath);
        File dstFile=new File(dstPath);
        FileChannel srcChannel=null;
        FileChannel dstChannel=null;
        //链接输入输出管道
        try {
            srcChannel=new FileInputStream(srcFile).getChannel();
            dstChannel=new FileOutputStream(dstFile).getChannel();
            //数据复制
            srcChannel.transferTo(0,srcChannel.size(),dstChannel);
            result=true;

        } catch (FileNotFoundException e) {
            System.out.println("exception in file not found");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("exception in io");
        }

        return result;
    }

    /**
     * 将String写入file
     */
    public static boolean saveStringToFile(String content,String filePath){
        boolean result=false;
        //content可以为“”不可以为null,filePath不能为空
       if(content==null|| TextUtils.isEmpty(filePath)){
            return result;
       }

       File file=new File(filePath);
        PrintWriter pw=null;

        //判断file是否存在，不存在新建
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                System.out.println("file create exception!");
                e.printStackTrace();
            }
        }

        //实例化printwrite,使用完close
        try {
            pw=new PrintWriter(file);
            pw.write(content);
            result=true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(pw!=null){
                pw.close();
            }
        }

        return result;
    }


    /**
     * 读取file内容
     */
    public static String readStringFromFile(String filePath){
        String result="";
        if(TextUtils.isEmpty(filePath)){
            return result;
        }

        File file=new File(filePath);
        if(file.exists()){

            try {
                StringBuffer sb=new StringBuffer();
                BufferedReader reader=new BufferedReader(new FileReader(file));
                while((result=reader.readLine())!=null){
                    sb.append(result);
                }
                return sb.toString();


            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        return result;
    }

    /**
     * 递归解压Zip
     * 针对zip下可能含有目录及目录下还有子目录和文件的情况
     * zipFileString 压缩文件字符串
     * folderPath 解压到的目标路径
     */
    public static boolean unpackZipFile(String zipFileString,String folderPath){
        boolean result =false;
        ZipFile zipFile=null;
        try {
            zipFile=new ZipFile(zipFileString);

        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取zipFile压缩文件中的所有元素集，并遍历,包括了目录下的目录和文件
        Enumeration zList=zipFile.entries();
        ZipEntry ze=null;
        byte[]bytes=new byte[1024];
        while (zList.hasMoreElements()){
            ze= (ZipEntry) zList.nextElement();
            //列举压缩文件中每个元素并进行判断文件还是目录
            if(ze.isDirectory()){//目录
                //新路径
                String dirstr=folderPath+ze.getName();
                //去空格
                dirstr.trim();
                //建目录
                File dirFile=new File(dirstr);
                dirFile.mkdir();
                //此轮完进入下一轮
                continue;
            }
            //如果ze是文件
            OutputStream os=null;
            FileOutputStream fos=null;
            File realFile=getRealFileName(folderPath,ze.getName());

            try {

                fos=new FileOutputStream(realFile);
                os=new BufferedOutputStream(fos);
                InputStream is=null;
                is=new BufferedInputStream(zipFile.getInputStream(ze));

                int readLine=0;
                while((readLine=is.read(bytes,0,1024))!=-1){
                    os.write(bytes,0,readLine);
                }

                is.close();
                os.close();
                zipFile.close();

                result=true;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }


        }


        return result;
    }

    /**
     * 从Zip的getName中获取系统可识别的路径名
     * filepath 解压到的目标路径
     * zipName 通过zipentry.getname获得的zipname
     */
    public static File getRealFileName(String filePath,String zipName){
        //先替换掉双斜杠，磁盘路径带有\\
        zipName=zipName.replace("\\","/");
        //按照单斜杠切分
        String[]dirs=zipName.split("/");
        //根据路径创建文件实例
        File file=new File(filePath);
        String subStr="";
        if(dirs.length>1){
            //dirs.length-1前面的都是目录，dirs.length-1是文件
            for(int i=0;i<dirs.length-1;i++){
                subStr=dirs[i];
                File subFile=new File(file,subStr);
            }
            //路径上的目录不全就makdir补全目录，再创建文件
            if(file.exists()){
                file.mkdir();
                subStr=dirs[dirs.length-1];
                file=new File(file,subStr);
                return file;
            }
        }else{
            file=new File(file,zipName);
        }

        return file;
    }


    /**
     * 压缩文件为zip
     * fs 需要压缩的文件集
     * zipFilePath 指定压缩文件的存储目录
     */
    public static boolean zipFiles(File[]fs,String zipFilePath){
        Boolean result=false;
        //判空
        if(fs==null){
            return result;
        }

        ZipOutputStream zos=null;
        try {
            //zip输出流
            zos=new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFilePath)));
            for(File file:fs){
                //对需要压缩的文件检查一遍
                if(file==null||!file.exists()){
                    continue;
                }
                //所有的压缩代码放置于recursionZip中
                if(file.isDirectory()){
                    recursionZip(zos, file, file.getName() + File.separator);
                }else{
                    recursionZip(zos,file,"");
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 递归热cursion压缩目录
     *zipout 压缩输出流
     * file 被压缩的文件
     * baseDir 压缩路径
     */
    public static void recursionZip(ZipOutputStream zipOut,File file,String baseDir){
        //判断file的类型
        if(file.isDirectory()){
            File[]files=file.listFiles();
            for(File inFile:files){
                if(inFile==null){
                    continue;
                }
                if(inFile.isDirectory())
                    baseDir = file.getName() + File.separator + inFile.getName() + File.separator;
                recursionZip(zipOut,inFile,baseDir);

            }
        }else{
            byte[]buf=new byte[1024];
            try {

                InputStream is=new BufferedInputStream(new FileInputStream(file));
                zipOut.putNextEntry(new ZipEntry(baseDir+file.getName()));
                int readLine=0;
                while((readLine=is.read(buf))!=-1){
                    zipOut.write(buf,0,readLine);
                }

                is.close();

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}
